package pl.edu.agh.ki.neuralnetwork.network;

import java.util.List;

import pl.edu.agh.ki.neuralnetwork.exceptions.NotEnoughLayersException;
import pl.edu.agh.ki.neuralnetwork.exceptions.NotEnoughNeuronsException;
import pl.edu.agh.ki.neuralnetwork.exceptions.OutOfRangeException;
import pl.edu.agh.ki.neuralnetwork.layer.BPAbstractLayer;
import pl.edu.agh.ki.neuralnetwork.layer.BPLastLayer;
import pl.edu.agh.ki.neuralnetwork.layer.BPInnerLayer;
import pl.edu.agh.ki.neuralnetwork.layer.Layer;
import pl.edu.agh.ki.neuralnetwork.neurons.InnerNeuron;
import pl.edu.agh.ki.neuralnetwork.neurons.InputNeuron;

public class BPNeuralNetworkImpl extends NeuralNetworkImpl {

	private List<Double> momentumList;
	private List<Double> learningSpeedList;
	public BPNeuralNetworkImpl(Layer<InputNeuron> inputLayer,
			List<Layer<InnerNeuron>> innerLayers)
			throws NotEnoughLayersException, NotEnoughNeuronsException {
		super(inputLayer, innerLayers);
	}
	/**
	 * function returns learning coefficient (it should be decreasing)
	 * 
	 * @param iteration
	 * @return
	 */
	private double n(int iteration) {
		int i=iteration/1000;
		if(i>learningSpeedList.size()-1)
			i = learningSpeedList.size()-1;
		return learningSpeedList.get(i);
	}
	private double n2(int iteration) {
		int i=iteration/1000;
		if(i>momentumList.size()-1)
			i = momentumList.size()-1;
		return momentumList.get(i);
	}
	@Override
	public void learn(List<Double> learningInput, List<Double> pattern, int iteration) {
		try {
//			System.out.println("uczenie BP");
//			System.out.println("warstwy przed uczeniem: ");
//			for (int i = 1; i < getLayersNumber(); i++) {
//				System.out.println("warstwa nr " + i);
//				System.out.println(getLayer(i));
//			}

			setInputSignals(learningInput.toArray(new Double[0]));
			
			compute();
			
			//oblicz bledy
			for (int i = getLayersNumber() - 1; i >= 1; i--) {
				if (i == getLayersNumber() - 1) {
					BPLastLayer lastLayer = (BPLastLayer) getLayer(i);
					lastLayer.computeErrors(pattern);
				} else {
					BPInnerLayer layer = (BPInnerLayer) getLayer(i);
					layer.computeErrors(getLayer(i + 1));
				}
			}
			
			//zmodyfikuj wagi			
			for (int i = getLayersNumber() - 1; i >= 1; i--) {
					BPAbstractLayer layer = (BPAbstractLayer) getLayer(i);
					layer.modifyWeights(getLayersNumber()-i,n(i),n2(i));
			}
			

		} catch (OutOfRangeException e) {
			e.printStackTrace();
		}
	}
	public void setLearningSpeedList(List<Double> learningSpeedList) {
		this.learningSpeedList = learningSpeedList;
		
	}
	public void setMomemntumList(List<Double> momentumList) {
		this.momentumList = momentumList;
		
	}
}
